package com.atguigu.security.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;

/**
 * @Description: SpringSecurity配置类
 * @Author: zhoufuzong
 * @Created: 2020-12-06 15:48:17
 * @Version: 1.1.0
 */

// 注意！这个类一定要放在自动扫描的包下，否则所有配置都不会生效！

// 将当前类标记为配置类
@Configuration

// 启用Web环境下权限控制功能
@EnableWebSecurity
public class WebAppSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DataSource dataSource;   // 注入数据源

    @Autowired
    private MyUserDetailsService userDetailsService;

//    @Autowired
//    private MyPasswordEncoderService passwordEncoderService;


    // 每次调用这个方法时会检查IOC 容器中时候有对应的bean,如果有就不会真正执行这个函数，因为bean默认是单例的
    // 可以使用@Scope(value= "") 注解控制是否单例
    @Bean
    public BCryptPasswordEncoder getBCryPasswordEncode(){
        return new BCryptPasswordEncoder();

    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {

//        auth
//                .inMemoryAuthentication()       //在内存中完成账号，密码检查
//                .withUser("tom")      // 指定账号
//                .password("123123")             // 指定密码
//                .roles("ADMIN","学徒")                 // 指定当前用户的角色
//                .and()
//                .withUser("jerry")      // 指定另外一个账号
//                .password("123123")
//                .authorities("UPDATE","内门弟子")            // 指定当前用户的权限
//                ;

        auth
                .userDetailsService(userDetailsService)
                .passwordEncoder(getBCryPasswordEncode())
        ;

    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        // 可去掉
//        tokenRepository.setCreateTableOnStartup(true);  // 启动服务器时创建表
//        tokenRepository.initDao();


       http
           .authorizeRequests()                         // 对请求进行授权
           .antMatchers("/index.jsp")       // 针对/index.jsp路径进行授权
           .permitAll() // 可以无条件访问
           .antMatchers("/layui/**")        // 针对layui 目录下所有资源进行授权
           .permitAll()
           .antMatchers("/level1/**")        // 针对level1/** 设置路径访问设置要求
           .hasRole("学徒")                               // 要求用户具备学徒角色才可以访问 // 底层 role 会增加 ROLE_ 前缀
           .antMatchers("/level2/**")        // 针对level2/** 设置路径访问设置要求
           .hasAuthority("内门弟子")                     // 要求具备内门弟子的权限 // hasAuthority 权限不会
           .and()
           .authorizeRequests(  )                       // 对请求进行授权
            .anyRequest()                               // 任意请求
           .authenticated()                             // 需要登录以后才可以访问
            .and()
            .formLogin()                                 // 使用表单形式登录
           // 关于loginPage()方法的特殊说明
           // 指定登录页的同时会影响到：“提交登录表单的地址，退出登录地址，登录失败的地址”
           // /index.jsp GET - the login form 去登录页面
           // /index.jsp POST - process the credentials and if valid authenticate the user 提交登录表单
           // /index.jsp?error GET - redirect here for failed authentication attempts 登录失败
           // /index.jsp?logout GET - redirect here after successfully logging out 退出登录
            .loginPage("/index.jsp")                    // 指定登录页面（不指定则访问springsercurity自带的页面
           // loginProcessingUrl()方法指定了登录地址，就会覆盖loginPage()方法宗设置的默认值/index.jsp POST
            .loginProcessingUrl("/do/login.html")           // 指定提交登录表单的地址
            .usernameParameter("loginAcct")                 // 定制 登录账号请求参数名
            .passwordParameter("userPswd")                  // 定制 登录密码请求参数名
            .defaultSuccessUrl("/main.html")                // 设置登录成功后默认前往的URL地址
//                .and()
//                .csrf()
//                .disable()                                      // 禁用csrf 功能
             .and()
            .logout()                                       // 开启退出功能
            .logoutUrl("/do/logout.html")                   // 指定处理退出请求的url地址
            .logoutSuccessUrl("/index.jsp")                 // 指定退出后前往的地址
            .and()
            .exceptionHandling()                           // 指定异常处理器
//            .accessDeniedPage("/to/no/auth/page.html")     // 访问被拒绝时前往的页面
            .accessDeniedHandler(new AccessDeniedHandler() {   // 定制403 页面
                @Override
                public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
                    httpServletRequest.setAttribute("message","抱歉！您无法访问此资源！@");
                    httpServletRequest.getRequestDispatcher("/WEB-INF/views/no_auth.jsp").forward(httpServletRequest,httpServletResponse);
                }
            })
            .and()
           .rememberMe()                // 开启记住我功能 -cookies
           .tokenRepository(tokenRepository);

    }
}

